package com.tradesolver;

import static com.google.appengine.api.taskqueue.TaskOptions.Builder.withUrl;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.Logger;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.taskqueue.Queue;
import com.google.appengine.api.taskqueue.QueueFactory;
import com.google.gson.Gson;

@SuppressWarnings("serial")
public class DataServlet extends HttpServlet {
  Logger logger = Logger.getLogger(DataServlet.class.getName());

  NumberFormat vf = new DecimalFormat("###,###,###,###.###");

  CsvService service = new CsvService();
  Dao dao = new Dao();
  Gson gson = new Gson();
  int index = 0;
  SimpleDateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");
  SimpleDateFormat mmm = new SimpleDateFormat("MMM");
  SimpleDateFormat MMddyyyy = new SimpleDateFormat("MM/dd/yyyy");

  @Override
  public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {

    String queueTicker = req.getParameter("queueTicker");
    if (queueTicker != null) {
      boolean overrideMaxDate = ("true".equals(req.getParameter("overrideMaxDate")));
      service.refreshTicker(queueTicker, overrideMaxDate);
      resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
      return;
    }

  }

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {

    String realtime = req.getParameter("update");
    if (realtime != null) {
      service.getAndStoreRealtime();
      resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
      return;
    }

    String ticker = req.getParameter("ticker");
    if (ticker != null) {
      String name = Companies.getName(ticker);
      if (name != null) {
        resp.setContentType("text/plain");
        resp.getWriter().println(name);
        logger.info("name=" + name);
        return;
      }
    }

    String candle = req.getParameter("candle");
    if (candle != null) {
      String data = getCandleData(candle);
      resp.setContentType("text/plain");
      resp.getWriter().print(data);
      resp.flushBuffer();
      return;
    }

    String magic = req.getParameter("magic");
    if (magic != null) {
      //resp.setContentType("text/html");
      List<MagicEntity> l;
      try {
        l = doMagic(resp);
      } catch (Exception e1) {
        throw new RuntimeException(e1);
      }
      req.setAttribute("infoList", l);
      
      String nextJSP = "/WEB-INF/jsp/magic.jsp";
      RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(nextJSP);
      try {
        dispatcher.forward(req,resp);
      } catch (ServletException e) {
        throw new RuntimeException(e);
      }

    }

    String report = req.getParameter("report");
    if (report != null) {
      resp.setContentType("text/plain");
      String aaData = getData(report);
      resp.getWriter().print(aaData);
      resp.flushBuffer();
      logger.info("aaData=" + aaData);
      return;
    }

    String financialsTicker = req.getParameter("financialsTicker");
    if (financialsTicker != null) {
      logger.info("financialsTicker=" + financialsTicker);
      FinancialsText ft = dao.getFinancialsText(financialsTicker);
      logger.info("financialsTicker=" + ft);

      resp.setContentType("text/html");
      resp.getWriter().println("<h1>" + financialsTicker + "</h1>");
      
      resp.getWriter().println("<h1>corporate</h1>");
      resp.getWriter().println(ft.corporate);

      resp.getWriter().println("<h1>incomeStatementA</h1>");
      resp.getWriter().println(ft.incomeStatementA);

      resp.getWriter().println("<h1>balanceSheetA</h1>");
      resp.getWriter().println(ft.balanceSheetA);

      resp.getWriter().println("<h1>cashFlowA</h1>");
      resp.getWriter().println(ft.cashFlowA);

      resp.getWriter().println("<h1>incomeStatementQ</h1>");
      resp.getWriter().println(ft.incomeStatementQ);

      resp.getWriter().println("<h1>balanceSheetQ</h1>");
      resp.getWriter().println(ft.balanceSheetQ);

      resp.getWriter().println("<h1>cashFlowQ</h1>");
      resp.getWriter().println(ft.cashFlowQ);
      return;
    }

    String financialsMap = req.getParameter("financialsMap");
    if (financialsMap != null) {
      logger.info("financialsMap=" + financialsMap);
      FinancialsMap fm = dao.getFinancialsMap(financialsMap);

      if (fm != null) {
        resp.setContentType("text/html");
        resp.getWriter().println("<h1>" + financialsMap + "</h1>");
        TreeMap<String, TreeMap<String, String>> mapInfo = retrieveInfo(fm.data);

        String lastKey = mapInfo.lastKey();
        resp.getWriter().println("<h1>" + lastKey + "</h1>");
        TreeMap<String, String> lastMap = mapInfo.get(lastKey);
        resp.getWriter().println("<table>");
        for (String key: lastMap.keySet()) {
          resp.getWriter().println("<tr><td>" + key + "</td><td>" + lastMap.get(key) + "</td></tr>");
        }
        resp.getWriter().println("</table>");
      }
      return;
    }

    String recache = req.getParameter("recache");
    String overrideMaxDate = req.getParameter("overrideMaxDate");
    if (recache != null) {
      Queue queue = QueueFactory.getDefaultQueue();
      for (int i = 0; i < Companies.tickers.length; i++) {
        if (overrideMaxDate != null) {
          queue.add(withUrl("/data").param("queueTicker", Companies.tickers[i]).param("overrideMaxDate", overrideMaxDate));
        } else {
          queue.add(withUrl("/data").param("queueTicker", Companies.tickers[i]));
        }
      }
    }

    resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
  }

  private float getFloat(String ticker, Map<String, String> props, String key) {
    float result = 0;
    String temp = props.get(key);
    try {
      result = vf.parse(temp).floatValue();
    } catch (Exception e) {
      logger.info("Error pulling " + key + " for " + ticker);
    }
    return result;
  }
  
    private List<MagicEntity> doMagic(HttpServletResponse resp) throws Exception {
      List<FinancialsMap> fms = dao.getFinancialsMaps();
      
      int million = 1000000;
      List<MagicEntity> comps = new ArrayList<MagicEntity>();
      for (FinancialsMap fm: fms) {
        TreeMap<String, TreeMap<String, String>> mapInfo = retrieveInfo(fm.data);
        TreeMap<String, String> props = mapInfo.get(fm.year);
        
        logger.fine("Processing " + fm.ticker);
        MagicEntity m = new MagicEntity(fm.ticker, fm.year);
//        try {
//          m.ebtExclude = vf.parse(props.get("EBT, EXCLUDING UNUSUAL ITEMS")).floatValue();
//          m.incomeLossEquityInvestment = vf.parse(props.get("Income (Loss) on Equity Investments")).floatValue();
//          m.currencyExchangeGainsLoss = vf.parse(props.get("Currency Exchange Gains (Loss)")).floatValue();
//          m.otherNonOperatingIncomeExpense = vf.parse(props.get("Other Non-Operating Income (Expenses)")).floatValue();
          m.currentAssets = getFloat(fm.ticker, props, "TOTAL CURRENT ASSETS") * million;
          m.currentLiabilities = getFloat(fm.ticker, props, "TOTAL CURRENT LIABILITIES") * million;
          m.operatingIncome = getFloat(fm.ticker, props, "OPERATING INCOME") * million;
          
          
          m.netInterestExpense = getFloat(fm.ticker, props, "NET INTEREST EXPENSE") * million;
          
          m.netFixedAssets = getFloat(fm.ticker, props, "NET PROPERTY PLANT AND EQUIPMENT") * million;
          m.cashAndEquivalents = getFloat(fm.ticker, props, "Cash and Equivalents") * million;
          
          m.longTermDebt = getFloat(fm.ticker, props, "Long-Term Debt") * million;
          
          m.marketCapitalization = getFloat(fm.ticker, props, "Market Capitalization");
          
//          m.ebit = 
          comps.add(m);
          
//        } catch (ParseException e) {
//          logger.severe("Error processing " + fm.ticker);
//          throw new IllegalStateException(e);
//        } catch (RuntimeException e) {
//          logger.severe("Error processing " + fm.ticker);
//          throw e;
          
//        }
        
      }
      return comps;
    
  }

    //TODO remove copy
    private TreeMap<String, TreeMap<String, String>> retrieveInfo(byte[] mapBytes) {
      TreeMap<String, TreeMap<String, String>> mapInfo;
      try {
        ByteArrayInputStream bis = new ByteArrayInputStream(mapBytes);
        ObjectInputStream in;
        in = new ObjectInputStream(bis);
        mapInfo = (TreeMap<String, TreeMap<String, String>>) in.readObject();
        in.close();
      } catch (IOException e) {
        throw new IllegalStateException(e);
      } catch (ClassNotFoundException e) {
        throw new IllegalStateException(e);
      }
      return mapInfo;
    }
    
  private String getCandleData(String ticker) {
    TreeMap<String, TreeMap<String, Quote>> companies = new TreeMap<String, TreeMap<String, Quote>>();
    companies = service.getCloseData(new String[] { ticker }, "20110101");

    TreeMap<String, Quote> data = companies.get(ticker);
    List<String> quoteStrings = new ArrayList<String>();
    for (String key : data.keySet()) {
      Quote quote = data.get(key);
      String date = key.substring(4, 6) + "/" + key.substring(6) + "/" + key.substring(0, 4);
      Object[] arr = new Object[] { date, quote.open, quote.high, quote.low, quote.close };
      quoteStrings.add(gson.toJson(arr));
    }

    StringBuilder builder = new StringBuilder();
    builder.append("[");
    for (String quoteString : quoteStrings) {
      builder.append(quoteString).append(",");
    }
    builder.append("]");

    String result = builder.toString();
    logger.fine(result);
    return result;
  } 

  private String getData(String report_tickers) {
    String[] tickers = report_tickers.split(",");
    Calendar now = Calendar.getInstance();
    // for (int i = 1; i <= now.get(Calendar.DAY_OF_YEAR); i++) {

    // accumulate set of dates (for headers)
    // accumulate companies

    TreeMap<String, TreeMap<String, Quote>> companies = new TreeMap<String, TreeMap<String, Quote>>();
    companies = service.getCloseData(tickers, "20110101");

    Calendar today = Calendar.getInstance();
    int currentMonth = today.get(Calendar.MONTH) + 1;

    TreeSet<String> dates = new TreeSet<String>();

    // summarise previous months
    for (String ticker : companies.keySet()) {
      TreeMap<String, Quote> quotes = companies.get(ticker);
      for (int summaryMonth = 1; summaryMonth < currentMonth; summaryMonth++) {
        today.set(Calendar.MONTH, summaryMonth - 1);
        today.set(Calendar.DAY_OF_MONTH, 1);

        String fromKey = yyyyMMdd.format(today.getTime());
        String month = mmm.format(today.getTime());
        logger.info("fromKey=" + fromKey);

        today.add(Calendar.MONTH, 1);
        String toKey = yyyyMMdd.format(today.getTime());
        logger.info("toKey=" + toKey);

        // double currentLow = Double.MAX_VALUE;
        Quote currentLowQuote = new Quote(null, 0f, 0f, Float.MAX_VALUE, 0f, 0f);
        SortedMap<String, Quote> subMap = quotes.subMap(fromKey, toKey);
        for (String key : subMap.keySet()) {
          Quote quote = subMap.get(key);

          if (quote.low < currentLowQuote.low) {
            // currentLow = quote.low;
            currentLowQuote = quote;
          }
        }
        dates.add(fromKey.substring(0, 6));
        quotes.put(fromKey.substring(0, 6), currentLowQuote);
      }
    }

    String currentMonthKeyFrom = yyyyMMdd.format(today.getTime());
    for (String ticker : companies.keySet()) {
      ticker = ticker.toUpperCase();
      TreeMap<String, Quote> quotes = companies.get(ticker);
      dates.addAll(quotes.tailMap(currentMonthKeyFrom).keySet());
    }

    StringBuilder sb = new StringBuilder();
    sb.append("{");
    sb.append("\"headers\":");
    Header[] headers = new Header[dates.size() + 1];
    headers[0] = new Header("Company");
    int i = 1;
    for (String date : dates) {
      // headers[i++] = new Header(date.substring(6) + "/" + date.substring(4,
      // 6));
      String displayDate = MONTHS[Integer.valueOf(date.substring(4, 6))];
      if (date.length() == 8) {
        displayDate = displayDate + date.substring(6);
      }
      headers[i++] = new Header(displayDate);
    }
    sb.append(gson.toJson(headers));

    sb.append(",");

    sb.append("\"aaData\":");
    int j = 0;
    String[][] rows = new String[companies.size()][];
    for (String ticker : companies.keySet()) {
      TreeMap<String, Quote> quotes = companies.get(ticker);
      String[] row = new String[dates.size() + 1];
      row[0] = ticker;
      i = 1;
      for (String date : dates) {
        row[i++] = ((quotes.get(date) != null) ? "" + quotes.get(date).low : "");
      }
      rows[j++] = row;
    }
    logger.fine("data is" + gson.toJson(rows));

    sb.append(gson.toJson(rows));

    sb.append("}");

    return sb.toString();
  }

  private static String[] MONTHS = new String[] { "", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
      "Oct", "Nov", "Dec" };

  private static class Header {
    String sTitle;

    public Header(String sTitle) {
      super();
      this.sTitle = sTitle;
    }
  }
}
